/**
 * @file crossover.c
 * @brief Genetic functions for combining 2 modules into 1
 * @author Scott Moyers
 * @date 2009
 */

#include "crossover.h"
#include "util.h"

#include <string.h>

/** crossover function pointer array to make selecting crossover functions easy */
CrossoverFunc crossover_functions[CROSS_FUNCS] = {
	one_point_crossover,
	two_point_crossover,
	uniform_crossover
};

CrossoverFunc get_crossover_func(const char *func)
{
	if (!strcmp(func, "one_point_crossover")) {
		return &one_point_crossover;
	} else if (!strcmp(func, "two_point_crossover")) {
		return &two_point_crossover;
	} else if (!strcmp(func, "uniform_crossover")) {
		return &uniform_crossover;
	}
	return NULL;
}

struct Module *one_point_crossover(dsfmt_t *dsfmt, struct Module const *p1, struct Module const *p2)
{
	int r;
	struct Module *child;
	assert(p1->num_gates == p2->num_gates);

	/* pick a point to crossover */
	r = (int) (dsfmt_genrand_close_open(dsfmt) * p1->num_gates);

	child = copy_module(p1);
	memcpy(&child->circuit[r], &p2->circuit[r],
	       sizeof(struct Gate) * (child->num_gates - r));
	child->id = next_module_id();

	return child;
}

struct Module *two_point_crossover(dsfmt_t *dsfmt, struct Module const *p1, struct Module const *p2)
{
	int rlo, rhi;
	struct Module *child;
	assert(p1->num_gates == p2->num_gates);

	/* pick 2 points to crossover */
	rlo = (int) (dsfmt_genrand_close_open(dsfmt) * p1->num_gates);
	rhi = (int) (dsfmt_genrand_close_open(dsfmt) * p1->num_gates);
	if (rlo > rhi) {
		swap(&rlo, &rhi);
	}

	child = copy_module(p1);
	memcpy(&child->circuit[rlo], &p2->circuit[rlo],
	       sizeof(struct Gate) * (rhi - rlo));
	child->id = next_module_id();

	return child;
}

struct Module *uniform_crossover(dsfmt_t *dsfmt, struct Module const *p1, struct Module const *p2)
{
	unsigned int i;
	struct Module *child;
	(void) dsfmt; /* remove unused parameter warning */
	assert(p1->num_gates == p2->num_gates);
	child = copy_module(p1);
	for(i = 1; i < p1->num_gates; i += 2) {
		child->circuit[i] = p2->circuit[i];
	}
	child->id = next_module_id();
	return child;
}
